import json
import sys

class ParsingException(Exception):
    def __init__(self, message=''):
        if message:
            message = ' - ' + message
        Exception.__init__(self, 'Malformed or incomplete log file' + message)

class LogParser(object):
    def __init__(self):
        self.working = []
        self.start_offset = 0

    def ParseFile(self, filename):
        with open(filename, 'r') as f:
            log_string = f.read().strip()
        return self.ParseString(log_string)

    def ParseString(self, log_string):
        """Returns an sequence containing timing information for a sequence of
        commands.

        Format returned: [command, command, command] where each command may
        have zero or more child-commands, in the 'children' key of the dict."""
        stack = []

        entries = json.loads(log_string)

        for i in range(0, len(entries)):
            entry = entries[i]
            try:
                self._Push(stack, entry)
            except:
                should_raise = True
                if i + 1 < len(entries):
                    next = entries[i + 1]
                    if next['timestamp'] == entry['timestamp']:
                        entries[i] = next
                        entries[i + 1] = entry
                        self._Push(stack, next)
                        should_raise = False
                if should_raise:
                    raise

        if self.working:
            raise ParsingException('Missing end of started commands')
        return stack

    def _Push(self, stack, log_entry):
        sys.stderr.write('%s\n' % log_entry)
        # log_entry is the raw webdriver log entry of form {timestamp:long,level:long,message:string}
        # entry is what we care about, of form {start:long,end:long,children:entry[],...}
        # message is the JSON decoding of log_entry's message
        entry = {'children': []}
        message = json.loads(log_entry['message'])
        is_start = message['startorend'] == 'start'
        del message['startorend']

        for (k,v) in message.items():
            entry[k] = v

        if not stack:
            if is_start:
                self.start_offset = log_entry['timestamp']
            else:
                raise ParsingException(
                    'First command was not the start of a command')

        timestamp = log_entry['timestamp'] - self.start_offset

        if is_start:
            entry['start'] = timestamp

        if not self.working:
            if is_start:
                self.working.append(entry)
                stack.append(entry)
                return
            else:
                raise ParsingException('Missing start of ended command')

        last_command = self.working[-1]

        if is_start:
            self.working.append(entry)
            last_command['children'].append(entry)
            return

        if not is_start and entry['event'] == last_command['event']:
            last_command['end'] = timestamp
            self.working.pop()
            return

        raise ParsingException('Encountered %s, working stack: %s' % (entry, self.working))
